package com.wiscamp.ninechapters.problems.domain.services;

import com.wiscamp.ninechapters.problems.domain.ProblemsErrorCodes;
import com.wiscamp.ninechapters.problems.domain.models.types.ProblemType;
import com.wiscamp.ninechapters.problems.domain.models.types.QuestionType;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemTypeRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.QuestionTypeRepository;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

/**
 * Problem Type Service
 *
 * @author yifeng
 */
@Service
public class ProblemTypeService extends BaseDomainService {

    private final ProblemTypeRepository problemTypeRepository;

    private final QuestionTypeRepository questionTypeRepository;

    private final String CACHE_NAME = "problemTypes";

    public ProblemTypeService(ProblemTypeRepository problemTypeRepository, QuestionTypeRepository questionTypeRepository) {
        this.problemTypeRepository = problemTypeRepository;
        this.questionTypeRepository = questionTypeRepository;
    }

    /**
     * Gets a problem type
     *
     * @param typeId
     * @return
     */
    @Cacheable(value = CACHE_NAME, key = "#typeId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public ProblemType getProblemType(@Min(0) long typeId) {
        var problemType = problemTypeRepository.getProblemType(typeId);
        if (Objects.isNull(problemType)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_TYPE_NOT_FOUND, typeId);
        }
        return problemType;
    }

    /*@HandleServiceException
    public ProblemType getProblemTypeByCode(@NotBlank String code) {
        return problemTypeRepository.getProblemTypeByCode(code);
    }

    @HandleServiceException
    public Collection<ProblemType> getProblemTypesByName(@NotBlank String name) {
        return problemTypeRepository.getProblemTypesByName(name);
    }

    @HandleServiceException
    public Collection<ProblemType> getAvailableProblemTypes() {
        return problemTypeRepository.getAvailableProblemTypes();
    }*/

    /**
     * Creates a problem type
     *
     * @param problemType
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#problemType.typeId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public ProblemType createProblemType(@NotNull ProblemType problemType, long operatorId) {
        var existedProblemType = problemTypeRepository.getProblemTypeByCode(problemType.getTypeCode());
        if (Objects.nonNull(existedProblemType)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_TYPE_CODE_EXISTS, problemType);
        }

        problemType.createBy(operatorId);
        return problemTypeRepository.saveProblemType(problemType);
    }

    /**
     * Updates a problem type
     *
     * @param problemType
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#problemType.typeId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public ProblemType updateProblemType(@NotNull ProblemType problemType, long operatorId) {
        var existedProblemType = getProblemType(problemType.getTypeId());
        existedProblemType = problemTypeRepository.getProblemTypeByCode(problemType.getTypeCode());
        if (Objects.nonNull(existedProblemType) && existedProblemType.getTypeId() != problemType.getTypeId()
                && existedProblemType.getTypeCode().equalsIgnoreCase(problemType.getTypeCode())) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_TYPE_CODE_EXISTS, problemType);
        }
        problemType.updateBy(operatorId);
        return problemTypeRepository.saveProblemType(problemType);
    }

    /**
     * Deletes a problem type
     *
     * @param typeId
     * @return
     */

    @CacheEvict(value = CACHE_NAME, key = "#typeId")
    @HandleServiceException
    public boolean deleteProblemType(@Min(0) long typeId, long operatorId) {
        var problemType = getProblemType(typeId);
        if (Objects.isNull(problemType)) return false;
        // 记录操作日志
        problemType.delete();
        problemType.updateBy(operatorId);
        problemTypeRepository.saveProblemType(problemType);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#typeId")
    @HandleServiceException
    public boolean enableProblemType(@Min(0) long typeId, long operatorId) {
        var problemType = getProblemType(typeId);
        if (Objects.isNull(problemType)) return false;
        // 记录操作日志
        problemType.enable();
        problemType.updateBy(operatorId);
        problemTypeRepository.saveProblemType(problemType);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#typeId")
    @HandleServiceException
    public boolean disableProblemType(@Min(0) long typeId, long operatorId) {
        var problemType = getProblemType(typeId);
        if (Objects.isNull(problemType)) return false;
        // 记录操作日志
        problemType.disable();
        problemType.updateBy(operatorId);
        problemTypeRepository.saveProblemType(problemType);
        return true;
    }

    /**
     * Creates a relation between a question type and a problem type
     *
     * @param problemTypeId
     * @param questionTypeId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#problemTypeId")
    @HandleServiceException
    public boolean bindQuestionType(long problemTypeId, long questionTypeId) {
        return problemTypeRepository.bindQuestionType(problemTypeId, questionTypeId);
    }

    @CacheEvict(value = CACHE_NAME, key = "#problemTypeId")
    @HandleServiceException
    public boolean unbindQuestionType(long problemTypeId, long questionTypeId) {
        return problemTypeRepository.unbindQuestionType(problemTypeId, questionTypeId);
    }

    @HandleServiceException
    public Collection<QuestionType> getQuestionTypesByProblemType(@Min(0) long problemTypeId) {
        var problemType = getProblemType(problemTypeId);
        if (Objects.nonNull(problemType) && !problemType.getQuestionTypeIds().isEmpty()) {
            return questionTypeRepository.getQuestionTypesByIds(problemType.getQuestionTypeIds());
        }
        return new ArrayList<>();
    }
}
